Skip to content

Reduce the frequency of breaking changes going forward#1031

Merged
kixelated merged 14 commits into
mainfrom
dev-setup
Mar 3, 2026
Merged

Reduce the frequency of breaking changes going forward#1031
kixelated merged 14 commits into
mainfrom
dev-setup

Conversation

@kixelated

@kixelated kixelated commented Mar 3, 2026

Copy link
Copy Markdown
Collaborator

Merging into main will require semver backwards compatibility.

kixelated and others added 2 commits March 2, 2026 16:12
Reduce semver violations and improve API clarity:

- Rename `close(err)` → `abort(err)` on all producer types to
  distinguish error termination from clean close
- Replace `TrackProducer::finish()` with `append_finish()` and
  `insert_finish(sequence)` for explicit control over the final group
- Rename `FrameProducer::write_chunk()` → `write()` for simplicity
- Add doc comments to public producer/consumer APIs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kixelated kixelated changed the title Dev setup API cleanup: rename close→abort, finish→append_finish, write_chunk→write Mar 3, 2026
@kixelated kixelated marked this pull request as ready for review March 3, 2026 01:12
@coderabbitai

coderabbitai Bot commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This pull request introduces semantic versioning enforcement into the project's CI/CD pipeline. Changes include: documentation of a branching strategy in CLAUDE.md; addition of cargo-semver-checks and release-plz tools to project dependencies; new Justfile targets for semver verification and version bumping; a new GitHub Actions workflow that runs semver checks on pull requests; and modification of the pull request trigger filter in the existing check workflow to apply to all pull requests rather than only those targeting the main branch.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: introducing semver compatibility checks and release workflow to reduce breaking changes when merging into main.
Description check ✅ Passed The description directly relates to the changeset, clearly stating that merging into main will require semver backwards compatibility, which aligns with the added semver checks and cargo-semver-checks tooling.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev-setup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rs/moq-lite/src/model/track.rs (1)

173-181: ⚠️ Potential issue | 🟠 Major

append_group currently hard-closes immediately after any insert_finish.

Line [175] blocks all appends when fin exists. If insert_finish is set to a future boundary, this prevents valid in-range appends before that boundary.

Suggested fix
 pub fn append_group(&mut self) -> Result<GroupProducer> {
 	let mut state = self.state.modify()?;
-	if state.fin.is_some() {
-		return Err(Error::Closed);
-	}
-
-	let now = tokio::time::Instant::now();
-	let sequence = state.max_sequence.map_or(0, |s| s + 1);
+	let sequence = state.max_sequence.map_or(0, |s| s + 1);
+	if let Some(fin) = state.fin
+		&& sequence >= fin
+	{
+		return Err(Error::Closed);
+	}
+	let now = tokio::time::Instant::now();
 	let group = Group { sequence }.produce();

Also applies to: 213-214

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rs/moq-lite/src/model/track.rs` around lines 173 - 181, The current
append_group function rejects all appends if state.fin.is_some(), but it should
only close appends once the fin boundary has passed; change the gating logic to
compute now = tokio::time::Instant::now() and return Err(Error::Closed) only
when state.fin.map_or(false, |f| now >= f) (i.e., fin exists and now is
at-or-after the boundary) instead of state.fin.is_some(); apply the same fix to
the other identical fin-check later in the file (the second occurrence near
lines 213-214) so both places use the time comparison rather than a simple
is_some() check.
🧹 Nitpick comments (1)
justfile (1)

371-374: Add a short rationale for --exclude libmoq.

Line 374 introduces a permanent-looking exclusion without context. A one-line reason here will prevent accidental misuse later.

Suggested small diff
-	cargo semver-checks check-release --workspace --exclude libmoq
+	# libmoq is intentionally excluded from crates.io semver checks.
+	cargo semver-checks check-release --workspace --exclude libmoq
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@justfile` around lines 371 - 374, Add a one-line rationale comment
immediately above the cargo semver-checks invocation that explains why --exclude
libmoq is needed (for example: libmoq is a non-published/internal crate or
intentionally unstable), so future readers understand the permanent-looking
exclusion; locate the line with "cargo semver-checks check-release --workspace
--exclude libmoq" and insert the brief explanatory comment above it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/dev.yml:
- Around line 18-23: Replace mutable action refs with immutable full commit
SHAs: change actions/checkout@v6 to actions/checkout@<commit-sha>,
DeterminateSystems/nix-installer-action@main,
DeterminateSystems/magic-nix-cache-action@main, and
DeterminateSystems/flake-checker-action@main to their respective full-length
commit SHAs, and add inline comments showing the original tag (e.g., // was `@v6`
or // was `@main`) for clarity; repeat the same SHA-pinning pattern in the other
workflows mentioned (main.yml, cachix.yml, release-js.yml, update-flake.yml).

In `@rs/moq-lite/src/model/track.rs`:
- Around line 204-209: append_finish (and similarly insert_finish) currently
overwrites fin unconditionally and can set an invalid final; change them to
validate and freeze the final boundary by: require that state.max_sequence is
Some(max) (return Err if not), require state.fin.is_none() (return Err if fin
already set), and then set state.fin = Some(max) (don’t accept sequence < max or
allow rewriting fin). Refer to the append_finish and insert_finish methods and
the state.max_sequence/state.fin fields when implementing these checks.
- Around line 74-79: The current logic returns Poll::Ready(None) as soon as
self.fin is set, which can prematurely terminate consumers; update the
Stream/iterator logic that currently checks self.fin (the block returning
Poll::Ready(None) and the similar checks in the methods using self.fin) to only
return Ready(None) when fin is set AND there are no pending groups and no future
groups can still be created (i.e., the internal queue/buffer is empty and
create_group would not accept any sequence < fin). Replace the early Ready(None)
returns with Poll::Pending (or equivalent) in the interim, and keep terminal
None only when self.fin.is_some() && queue.is_empty() && no allowed late
sequences remain so next_group/get_group won't stop early. Ensure the same
change is applied to the other occurrences (the checks around lines referencing
self.fin, create_group, next_group, get_group).

---

Outside diff comments:
In `@rs/moq-lite/src/model/track.rs`:
- Around line 173-181: The current append_group function rejects all appends if
state.fin.is_some(), but it should only close appends once the fin boundary has
passed; change the gating logic to compute now = tokio::time::Instant::now() and
return Err(Error::Closed) only when state.fin.map_or(false, |f| now >= f) (i.e.,
fin exists and now is at-or-after the boundary) instead of state.fin.is_some();
apply the same fix to the other identical fin-check later in the file (the
second occurrence near lines 213-214) so both places use the time comparison
rather than a simple is_some() check.

---

Nitpick comments:
In `@justfile`:
- Around line 371-374: Add a one-line rationale comment immediately above the
cargo semver-checks invocation that explains why --exclude libmoq is needed (for
example: libmoq is a non-published/internal crate or intentionally unstable), so
future readers understand the permanent-looking exclusion; locate the line with
"cargo semver-checks check-release --workspace --exclude libmoq" and insert the
brief explanatory comment above it.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0eb204a and 5366cee.

📒 Files selected for processing (16)
  • .github/workflows/dev.yml
  • .github/workflows/main.yml
  • CLAUDE.md
  • flake.nix
  • justfile
  • rs/hang/src/container/frame.rs
  • rs/moq-lite/src/ietf/subscriber.rs
  • rs/moq-lite/src/lite/subscriber.rs
  • rs/moq-lite/src/model/broadcast.rs
  • rs/moq-lite/src/model/frame.rs
  • rs/moq-lite/src/model/group.rs
  • rs/moq-lite/src/model/state.rs
  • rs/moq-lite/src/model/track.rs
  • rs/moq-mux/src/catalog.rs
  • rs/moq-mux/src/import/fmp4.rs
  • rs/moq-relay/src/cluster.rs

Comment thread .github/workflows/dev.yml
Comment on lines +18 to +23
uses: actions/checkout@v6

- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- uses: DeterminateSystems/flake-checker-action@main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "dev.yml" -o -name "dev.yaml" | head -20

Repository: moq-dev/moq

Length of output: 82


🏁 Script executed:

cat -n .github/workflows/dev.yml

Repository: moq-dev/moq

Length of output: 936


🏁 Script executed:

find .github/workflows -name "*.yml" -o -name "*.yaml" | head -20

Repository: moq-dev/moq

Length of output: 295


🏁 Script executed:

for file in .github/workflows/*.yml; do
  echo "=== $file ===" 
  grep -n "uses:" "$file" | head -20
done

Repository: moq-dev/moq

Length of output: 2013


🌐 Web query:

GitHub Actions best practices pinning commit SHA vs version tags security

💡 Result:

Pinning strategy: commit SHA vs tags (security-focused)

Recommended default: pin to a full-length commit SHA

  • GitHub’s security guidance says pinning to a full-length commit SHA is currently the only way to use an action as an immutable release, reducing the risk from repo compromise and tag retargeting (with the practical caveat to ensure the SHA is from the upstream repo, not a fork). [1]
  • GitHub’s Well-Architected guidance likewise calls full SHAs the most reliable immutable reference, noting tag immutability can depend on repo settings. [2]

Example:

- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

(Adding the version comment is explicitly recommended to keep reviews/audits sane.) [2]

When a tag can be OK: only if it’s an “immutable release/tag”

  • GitHub’s immutable releases lock the release assets and the associated Git tag so the tag can’t be moved/deleted after publication, which is meant to prevent supply-chain tampering of published releases. [3]
  • If an action author is using immutable releases, pinning to that release tag can be an acceptable “human-friendly” immutable reference; otherwise, tags (including v1, v4, etc.) may be intentionally moved to point at newer commits. [4]

Operational best practices

  • Prefer “SHA that corresponds to a release tag” (not a random commit on main) so you get immutability and clearer provenance; GitHub specifically recommends tying SHAs to tagged releases and labeling them. [2]
  • Use Dependabot version updates for GitHub Actions so SHA-pinned actions still get automated update PRs instead of silently floating to new code. [5]
  • If you’re an org/enterprise admin, enforce SHA pinning via policy (GitHub added explicit enforcement so workflows fail if actions aren’t pinned to a full SHA). [6]

Practical rule of thumb

  • Most repos: pin every third-party uses: to a full SHA.
  • Only pin a tag if you’ve verified it’s backed by GitHub immutable releases for that action—and you’re confident it can’t be retargeted. [3][4]

Pin GitHub Actions to immutable commit SHAs instead of moving refs.

Lines 18 and 20-22 use mutable refs (@v6, @main). GitHub's security guidance confirms that pinning to full-length commit SHAs is the only way to achieve immutability and prevent tag retargeting attacks. Version tags like @v6 and @main can be moved by maintainers after release, creating supply-chain exposure.

Update each action to its commit SHA with a version comment for clarity:

Example:
-      - uses: actions/checkout@v6
+      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

-      - uses: DeterminateSystems/nix-installer-action@main
-      - uses: DeterminateSystems/magic-nix-cache-action@main
-      - uses: DeterminateSystems/flake-checker-action@main
+      - uses: DeterminateSystems/nix-installer-action@<commit-sha> # v0.x.x
+      - uses: DeterminateSystems/magic-nix-cache-action@<commit-sha> # v1.x.x
+      - uses: DeterminateSystems/flake-checker-action@<commit-sha> # v0.x.x

Note: This pattern appears in multiple workflows; apply the same hardening to main.yml, cachix.yml, release-js.yml, and update-flake.yml.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/dev.yml around lines 18 - 23, Replace mutable action refs
with immutable full commit SHAs: change actions/checkout@v6 to
actions/checkout@<commit-sha>, DeterminateSystems/nix-installer-action@main,
DeterminateSystems/magic-nix-cache-action@main, and
DeterminateSystems/flake-checker-action@main to their respective full-length
commit SHAs, and add inline comments showing the original tag (e.g., // was `@v6`
or // was `@main`) for clarity; repeat the same SHA-pinning pattern in the other
workflows mentioned (main.yml, cachix.yml, release-js.yml, update-flake.yml).

Comment thread rs/moq-lite/src/model/track.rs Outdated
Comment thread rs/moq-lite/src/model/track.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
rs/moq-lite/src/model/track.rs (1)

214-226: insert_finish(sequence) accepts mismatched expected boundaries.

Right now sequence > max_sequence succeeds but still freezes to max_sequence. That can silently finalize earlier than the caller expected. Consider enforcing exact match (sequence == max) or renaming/docs to reflect “minimum expected” semantics.

💡 Suggested fix (strict expected-boundary validation)
-		if state.fin.is_some() || sequence < max {
+		if state.fin.is_some() || sequence != max {
 			return Err(Error::Closed);
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rs/moq-lite/src/model/track.rs` around lines 214 - 226, The insert_finish
method currently allows sequence > max_sequence and still sets state.fin to
max_sequence, which can silently finalize earlier than the caller expects;
change the validation in insert_finish (function insert_finish, variables
sequence, state.max_sequence, and state.fin) to require an exact match by
returning Err(Error::Closed) unless sequence == max (i.e., replace the current
condition that checks sequence < max with a strict equality check), then set
state.fin = Some(max) as before and preserve the existing error path when fin is
already set; also update the doc comment to state that insert_finish requires
the caller to pass the exact final boundary.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rs/moq-lite/src/model/track.rs`:
- Around line 647-648: The async test attribute needs to include start_paused =
true to follow the repo convention; update the #[tokio::test] annotation on the
next_group_finishes_without_waiting_for_gaps test (and the other new async test
referenced around lines 665-666) to #[tokio::test(start_paused = true)] so the
tests run with the tokio time paused and can use simulated time APIs.
- Line 173: The increment of state.max_sequence (sequence =
state.max_sequence.map_or(0, |s| s + 1)) can overflow; in the append_group path
replace the naive +1 with checked_add on state.max_sequence and, if checked_add
returns None (u64::MAX), return an appropriate error from append_group instead
of allowing overflow/wrap. Update any code that uses the resulting sequence
variable (and any state update of max_sequence) to handle the
Result/early-return error and keep the original semantics when checked_add
succeeds.

---

Nitpick comments:
In `@rs/moq-lite/src/model/track.rs`:
- Around line 214-226: The insert_finish method currently allows sequence >
max_sequence and still sets state.fin to max_sequence, which can silently
finalize earlier than the caller expects; change the validation in insert_finish
(function insert_finish, variables sequence, state.max_sequence, and state.fin)
to require an exact match by returning Err(Error::Closed) unless sequence == max
(i.e., replace the current condition that checks sequence < max with a strict
equality check), then set state.fin = Some(max) as before and preserve the
existing error path when fin is already set; also update the doc comment to
state that insert_finish requires the caller to pass the exact final boundary.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5366cee and 230bd8a.

📒 Files selected for processing (2)
  • justfile
  • rs/moq-lite/src/model/track.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • justfile

Comment thread rs/moq-lite/src/model/track.rs Outdated
Comment thread rs/moq-lite/src/model/track.rs
@kixelated kixelated enabled auto-merge (squash) March 3, 2026 04:12
@kixelated kixelated changed the title API cleanup: rename close→abort, finish→append_finish, write_chunk→write API tweaks: rename close→abort, write_chunk→write Mar 3, 2026
@kixelated kixelated changed the title API tweaks: rename close→abort, write_chunk→write Reduce the frequency of breaking changes going forward Mar 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rs/moq-lite/src/model/broadcast.rs`:
- Around line 185-189: BroadcastDynamic::abort currently only calls
self.state.abort(err) and leaves entries in self.state.requests (so
queued/pending track producers remain alive); to fix, before closing broadcast
state iterate and cancel all pending requests: drain or take ownership of
self.state.requests and for each request call its cancellation method (e.g.,
request.abort(err.clone()) or send the error down its response channel) to
ensure their producers are closed, then call self.state.abort(err); update Error
usage to allow cloning or accept &Error if needed and ensure requests are
removed from state (use drain() or clear()) so no pending producers remain.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f9658d and c8de69d.

📒 Files selected for processing (9)
  • CLAUDE.md
  • flake.nix
  • justfile
  • rs/hang/src/container/consumer.rs
  • rs/moq-lite/src/ietf/subscriber.rs
  • rs/moq-lite/src/lite/subscriber.rs
  • rs/moq-lite/src/model/broadcast.rs
  • rs/moq-lite/src/model/frame.rs
  • rs/moq-lite/src/model/track.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • flake.nix
  • justfile

Comment thread rs/moq-lite/src/model/broadcast.rs
BroadcastDynamic::abort now cascades to child tracks and drains pending
requests, matching BroadcastProducer::abort behavior. Previously pending
TrackProducers were left alive and the Drop handler couldn't clean up
since the state was already closed.

Track finish now stores final_sequence as one past max_sequence (the
total number of groups), allowing finish() on empty tracks. Renamed
`fin` to `final_sequence` for clarity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rs/moq-lite/src/model/broadcast.rs (1)

348-349: ⚠️ Potential issue | 🟡 Minor

Use start_paused = true for this async tokio test.

The closed async test uses #[tokio::test] instead of #[tokio::test(start_paused = true)].

✅ Minimal fix
-	#[tokio::test]
+	#[tokio::test(start_paused = true)]
 	async fn closed() {

As per coding guidelines **/*.rs: Use async/await with #[tokio::test(start_paused = true)] for async tests in Rust to simulate time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rs/moq-lite/src/model/broadcast.rs` around lines 348 - 349, The async test
function closed is missing the Tokio test option to pause time; update its
attribute from #[tokio::test] to #[tokio::test(start_paused = true)] so the test
uses a paused clock for simulated time; locate the closed function declaration
in broadcast.rs and change the attribute above the async fn closed()
accordingly.
🧹 Nitpick comments (1)
rs/moq-lite/src/model/broadcast.rs (1)

109-125: Consolidate duplicated abort cascade logic into one helper.

BroadcastProducer::abort and BroadcastDynamic::abort currently duplicate the same cascade logic. Centralizing this reduces drift risk when abort semantics evolve.

♻️ Suggested refactor
+fn abort_broadcast_state(state: &mut State, err: Error) {
+	// Cascade abort to all child tracks.
+	for weak in state.tracks.values() {
+		weak.abort(err.clone());
+	}
+
+	// Abort pending dynamic requests.
+	for mut request in state.requests.drain(..) {
+		request.abort(err.clone()).ok();
+	}
+
+	state.abort(err);
+}
+
 impl BroadcastProducer {
 	pub fn abort(&mut self, err: Error) -> Result<(), Error> {
 		let mut state = self.state.modify()?;
-
-		// Cascade abort to all child tracks.
-		for weak in state.tracks.values() {
-			weak.abort(err.clone());
-		}
-
-		// Abort any pending dynamic track requests.
-		for mut request in state.requests.drain(..) {
-			request.abort(err.clone()).ok();
-		}
-
-		state.abort(err);
+		abort_broadcast_state(&mut state, err);
 		Ok(())
 	}
 }
 
 impl BroadcastDynamic {
 	pub fn abort(&mut self, err: Error) -> Result<(), Error> {
 		let mut state = self.state.modify()?;
-
-		// Cascade abort to all child tracks.
-		for weak in state.tracks.values() {
-			weak.abort(err.clone());
-		}
-
-		// Abort any pending dynamic track requests.
-		for mut request in state.requests.drain(..) {
-			request.abort(err.clone()).ok();
-		}
-
-		state.abort(err);
+		abort_broadcast_state(&mut state, err);
 		Ok(())
 	}
 }

Also applies to: 185-200

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rs/moq-lite/src/model/broadcast.rs` around lines 109 - 125, Both
BroadcastProducer::abort and BroadcastDynamic::abort duplicate the same cascade
logic; extract that repeated sequence (iterating state.tracks to call
weak.abort(...), draining state.requests and calling request.abort(...), then
state.abort(...)) into a single helper method (e.g.,
BroadcastState::cascade_abort or Broadcast::cascade_abort) and call it from both
BroadcastProducer::abort and BroadcastDynamic::abort instead of duplicating the
code; ensure the helper takes &mut self and an Error parameter and uses the
existing weak.abort(err.clone()) and request.abort(err.clone()) calls so
behavior remains identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@rs/moq-lite/src/model/broadcast.rs`:
- Around line 348-349: The async test function closed is missing the Tokio test
option to pause time; update its attribute from #[tokio::test] to
#[tokio::test(start_paused = true)] so the test uses a paused clock for
simulated time; locate the closed function declaration in broadcast.rs and
change the attribute above the async fn closed() accordingly.

---

Nitpick comments:
In `@rs/moq-lite/src/model/broadcast.rs`:
- Around line 109-125: Both BroadcastProducer::abort and BroadcastDynamic::abort
duplicate the same cascade logic; extract that repeated sequence (iterating
state.tracks to call weak.abort(...), draining state.requests and calling
request.abort(...), then state.abort(...)) into a single helper method (e.g.,
BroadcastState::cascade_abort or Broadcast::cascade_abort) and call it from both
BroadcastProducer::abort and BroadcastDynamic::abort instead of duplicating the
code; ensure the helper takes &mut self and an Error parameter and uses the
existing weak.abort(err.clone()) and request.abort(err.clone()) calls so
behavior remains identical.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c8de69d and 3a3220b.

📒 Files selected for processing (2)
  • rs/moq-lite/src/model/broadcast.rs
  • rs/moq-lite/src/model/track.rs

kixelated and others added 5 commits March 2, 2026 22:14
Tests that use sleep/timeout now call tokio::time::pause() explicitly.
Tests that don't need time simulation use plain #[tokio::test].

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 3, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.github/workflows/semver.yml (1)

11-14: Consider renaming the job for clarity.

The job is named check with display name "Check", which is identical to the job in check.yml. This could cause confusion in the GitHub Actions UI when viewing PR status checks. Consider using a more descriptive name like semver or "Semver Check".

♻️ Proposed rename for clarity
 jobs:
-  check:
-    name: Check
+  semver:
+    name: Semver Check
     runs-on: ubuntu-latest
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/semver.yml around lines 11 - 14, The job in
.github/workflows/semver.yml is named "check" with display name "Check", which
duplicates another job and is confusing; rename the job identifier from check to
semver (or similar unique id) and change the runs-on display name to "Semver
Check" (update the job key `check` and the `name: Check` field) so the workflow
uses a distinct job id and a clear UI label.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/semver.yml:
- Around line 11-14: The job in .github/workflows/semver.yml is named "check"
with display name "Check", which duplicates another job and is confusing; rename
the job identifier from check to semver (or similar unique id) and change the
runs-on display name to "Semver Check" (update the job key `check` and the
`name: Check` field) so the workflow uses a distinct job id and a clear UI
label.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a3220b and 03e6463.

📒 Files selected for processing (4)
  • .github/workflows/check.yml
  • .github/workflows/semver.yml
  • CLAUDE.md
  • justfile
🚧 Files skipped from review as they are similar to previous changes (2)
  • justfile
  • CLAUDE.md

@kixelated kixelated disabled auto-merge March 3, 2026 20:30
@kixelated kixelated merged commit 64c6679 into main Mar 3, 2026
2 checks passed
@kixelated kixelated deleted the dev-setup branch March 3, 2026 20:30
phoewass pushed a commit to phoewass/moq that referenced this pull request Apr 3, 2026
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant